#include <Arduino.h>
#include <WiFi.h>
#include "PubSubClient.h"   ////A client library for MQTT messaging.
#include <Wire.h>
#include "MAX30105.h"
#include "heartRate.h"  
#include "mpu6050.h"

MAX30105 particleSensor;

/* 连接WIFI SSID和密码 ，需要修改*/
#define WIFI_SSID         "ESP8266"//换成自己的热点名称
#define WIFI_PASSWD       "12345678"//换成自己的热点密码

/* 设备的三元组信息，需要修改*/
#define PRODUCT_KEY       "a1P3aASA4ar"
#define DEVICE_NAME       "gangzi"
#define DEVICE_SECRET     "d6f8c3e182fcb089afa530df814a0126"
#define REGION_ID         "cn-shanghai"

/* 线上环境域名和端口号，不需要改 */
#define MQTT_SERVER       PRODUCT_KEY".iot-as-mqtt."REGION_ID ".aliyuncs.com"
#define MQTT_PORT         1883
#define MQTT_USRNAME      DEVICE_NAME"&"PRODUCT_KEY

/* 阿里云MQTT的连接参数，需要修改 */
#define CLIENT_ID         "a1P3aASA4ar.gangzi|securemode=2,signmethod=hmacsha256,timestamp=1750351046167|"
#define MQTT_PASSWD       "944e48e591e80c58f7f5023f86b7ce191197e60c506deed334ee5f97c2d604b7"

//宏定义订阅主题，不需要修改
#define ALINK_BODY_FORMAT         "{\"id\":\"littlecoke\",\"version\":\"1.0\",\"method\":\"thing.event.property.post\",\"params\":%s}"
#define ALINK_TOPIC_PROP_POST     "/sys/" PRODUCT_KEY "/" DEVICE_NAME "/thing/event/property/post"

unsigned long lastMs = 0;

WiFiClient espClient;
PubSubClient  client(espClient);

// 心率相关变量
static unsigned long lastBeat = 0;
static int beatsPerMinute = 0;
static int beatAvg = 0;
#define RATE_SIZE 4 // 平均心率窗口
int rate[RATE_SIZE]; // 存储最近的心率值
byte rateSpot = 0;
long lastIRValue = 0;

// MPU6050 I2C引脚
#define MPU6050_SDA 18
#define MPU6050_SCL 19
// 定义独立I2C对象
TwoWire I2C_MPU6050(1);
// 创建MPU6050对象
MPU6050 mpu6050(&I2C_MPU6050);

//连接wifi
void wifiInit()
{
    WiFi.begin(WIFI_SSID, WIFI_PASSWD);
    while (WiFi.status() != WL_CONNECTED)
    {
        delay(1000);
        Serial.println("WiFi not Connect");
    }
}

//mqtt连接
void mqttCheckConnect()
{
    while (!client.connected())
    {
        Serial.println("Connecting to MQTT Server ...");
        if(client.connect(CLIENT_ID, MQTT_USRNAME, MQTT_PASSWD))
        {
          Serial.println("MQTT Connected!");
        }
        else{
           Serial.print("MQTT Connect err:");
            Serial.println(client.state());
            delay(5000);
          }
        
    }
}

//上传心率数据
void mqttIntervalPost()
{
    char param[128];
    char jsonBuf[256];
    // 上报心率、跌倒、静止状态（1或0）
    sprintf(param, "{\"heartRate\":%d,\"fallDetected\":%d,\"stillDetected\":%d}",
        beatAvg,
        mpu6050.isFallDetected(),
        mpu6050.isStillDetected());
    sprintf(jsonBuf, ALINK_BODY_FORMAT, param);
    Serial.println(jsonBuf);
    boolean b = client.publish(ALINK_TOPIC_PROP_POST, jsonBuf);
    if(b){
      Serial.println("publish data success"); 
    }else{
      Serial.println("publish data fail"); 
    }
}

void setup()
{
  Serial.begin(9600);
    if (particleSensor.begin(Wire, I2C_SPEED_FAST) == false) //Use default I2C port, 400kHz speed
  {
    Serial.println("MAX30105 was not found. Please check wiring/power. ");
    while (1);
  }
  particleSensor.setup(0x1F, 4, 2, 100, 411, 4096); // 推荐参数：电流、平均、模式、采样率、脉宽、ADC
  particleSensor.enableDIETEMPRDY();
  wifiInit();
  client.setServer(MQTT_SERVER, MQTT_PORT);   /* 连接MQTT服务器 */
  // 初始化心率数组
  for (byte i = 0; i < RATE_SIZE; i++) rate[i] = 0;
  // 初始化MPU6050 I2C
  I2C_MPU6050.begin(MPU6050_SDA, MPU6050_SCL, 400000); // 400kHz
  if (!mpu6050.begin()) {
    Serial.println("MPU6050 init failed!");
    while (1);
  }
}

void loop()
{
  long irValue = particleSensor.getIR(); // 读取红外数据

  if (irValue > 50000) // 有手指遮挡时才检测心率
  {
    if (checkForBeat(irValue)) {
      unsigned long delta = millis() - lastBeat;
      lastBeat = millis();
      beatsPerMinute = 60 * 1000 / delta;
      if (beatsPerMinute < 255 && beatsPerMinute > 20) {
        rate[rateSpot++] = beatsPerMinute; // 存入数组
        rateSpot %= RATE_SIZE;
        // 计算平均心率
        int sum = 0;
        for (byte i = 0; i < RATE_SIZE; i++) sum += rate[i];
        beatAvg = sum / RATE_SIZE;
      }
      Serial.print(" BPM=");
      Serial.print(beatsPerMinute);
      Serial.print(" Avg BPM=");
      Serial.println(beatAvg);
    }
  } else {
 
    beatAvg = 0;
  }

  // 无论如何都更新mpu6050并输出数据
  mpu6050.update();

  // 事件触发上传：跌倒或静止
  if (mpu6050.fallEvent || mpu6050.stillEvent) {
      mqttCheckConnect();
      mqttIntervalPost();
      mpu6050.clearEvent();
  }

  if (millis() - lastMs >= 5000)
  {
      lastMs = millis();
      mqttCheckConnect(); 
      /* 上报 */
      mqttIntervalPost();
  }
  client.loop();
  delay(20); // 心率检测建议较高采样频率
} 
